package com.cat.orm.core.db.repository;

import com.cat.orm.core.base.BasePo;
import com.cat.orm.core.base.Clip;
import com.cat.orm.core.db.repository.dao.DefaultDao;
import com.cat.orm.core.db.repository.dao.IBaseDao;
import com.github.benmanes.caffeine.cache.AsyncCache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.*;
import java.util.concurrent.*;


/**
 * 异步的数据仓库, 使用单线程对业务线程对数据库的操作进行排队处理.
 * 如果对其做异步的处理, 对单条数据的处理很友好, 但是对批量操作的数据处理很不适合.
 * 缓存排队处理, 重试这种设计不适合关系型数据库的表结构设计, 适合非关系型的数据.
 * @author Jeremy Feng
 * @date 2024/8/25 00:23
 */
public class DataRepositoryAsync<V extends BasePo> implements IAsyncRepository<V> {

    /**
     * Dao, 可以由开发者自定义dao, 默认是阻塞的dao实现.
     */
    private final IBaseDao<V> dao;

    /**
     * key: 主键
     * value: 数据夹子
     */
    private final AsyncCache<Object, Clip<V>> cache = Caffeine.newBuilder()
            .expireAfterWrite(10, TimeUnit.MINUTES)  // 缓存过期时间
            .maximumSize(10_00)  // 最大缓存条目数
            .buildAsync();
    //private final ConcurrentMap<Object, Clip<V>> clips = new ConcurrentHashMap<>();

    private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

    public DataRepositoryAsync(IBaseDao<V> dao) {
        this.dao = dao;
        this.executor.scheduleAtFixedRate(() -> {
            this.tick();
        }, 1, 1, TimeUnit.MINUTES);
    }

    public DataRepositoryAsync(JdbcTemplate jdbcTemplate) {
        this.dao = new DefaultDao<>(jdbcTemplate);
        this.executor.scheduleAtFixedRate(() -> {
            this.tick();
        }, 1, 1, TimeUnit.MINUTES);
    }

    /**
     * 定时处理任务
     */
    public void tick(){
//        Set<String> keySet = commonDaoMap.keySet();
//        for (String key : keySet) {
//            DefaultDao<BasePo> dao = commonDaoMap.get(key);
//            //dao.processTasks();
//        }
    }

    /**
     * 查询数据, 从数据夹子中查询, 查询不到则从数据库查询.
     * @param key 主键
     * @return 异步返回数据
     */
    @Override
    public CompletableFuture<V> selectByPrimaryKey(Object key) {
        CompletableFuture<Clip<V>> future = cache.getIfPresent(key);
        if (future != null) {
            return future.thenApply(Clip::getData);
        }else{
            // 如果缓存中没有数据，异步查询数据库
            return CompletableFuture.supplyAsync(() -> dao.selectByKey(key));
        }
    }

    @Override
    public CompletableFuture<List<V>> selectAll() {
        return CompletableFuture.supplyAsync(dao::selectAll);
    }

    @Override
    public CompletableFuture<List<V>> selectByIndex(Object[] objs) {
        return CompletableFuture.supplyAsync(()->dao.selectByIndex(objs));
    }

    @Override
    public CompletableFuture<List<V>> selectByIndex(String[] props, Object[] objs) {
        return CompletableFuture.supplyAsync(()->dao.selectByIndex(props, objs));
    }

    @Override
    public CompletableFuture<Void> insert(V po) {
        String key = Arrays.toString(po.keyAndIndexValues());
        //if not cached, create, and cache.
        return CompletableFuture.runAsync(() -> {
            cache.get(key, k -> new Clip<>(po));
        });
    }

    @Override
    public CompletableFuture<Void> replace(V po) {
        String key = Arrays.toString(po.keyAndIndexValues());
        return CompletableFuture.runAsync(() -> {
            cache.get(key, k -> new Clip<>(po));
        });
    }

    @Override
    public CompletableFuture<Void> update(V po) {
        String key = Arrays.toString(po.keyAndIndexValues());
        return CompletableFuture.runAsync(() -> {
            cache.get(key, k -> new Clip<>(po));
        });
    }

    @Override
    public CompletableFuture<int[]> insertBatch(Collection<V> basePos) {
        return null;
    }

    @Override
    public CompletableFuture<int[]> updateBatch(Collection<V> basePos) {
        return null;
    }

    @Override
    public CompletableFuture<Integer> delete(V po) {
        return null;
    }

    @Override
    public CompletableFuture<Integer> deleteAll() {
        return null;
    }

    @Override
    public CompletableFuture<int[]> deleteBatch(Collection<V> basePos) {
        return null;
    }
}
